M1 Macで学ぶARMアセンブリプログラミング
code:return255.c
int main() {
return 255;
}
実行してみる
code:sh
$ gcc return255.c -o return255
$ ./return255
$ echo $?
255
C言語からアセンブリソースを生成する
code:sh
$ gcc -S return255.c
code:return255.s
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 12, 0 sdk_version 13, 1
.globl _main ; -- Begin function main
.p2align 2
_main: ; @main
.cfi_startproc
; %bb.0:
.cfi_def_cfa_offset 16
ret
.cfi_endproc
; -- End function
.subsections_via_symbols
不要そうなコードを削除してみた。
w0 にセットした値が戻り値として使われるっぽい。
code:return255.s
.text
.globl _main
.p2align 2
_main:
; スタック領域を16バイト確保
# 0番レジスタの値をスタックに待避
# w0 へ即値 255 をセット
; スタックを元に戻す
# 呼び出し元へ戻る
ret
code:fib.c
// hello.c
int fib(int n) {
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}
int i = n - 1;
int j = n - 2;
int fi = fib(i);
int fj = fib(j);
return fi + fj;
}
int main() {
int n = 10;
return fib(n);
}
実行してみる
code:sh
$ gcc fib.c -o fib
$ ./fib
$ echo $?
55
code:fib.s
.text
.globl _fib
.p2align 2
_fib:
stp x29, x30, sp, #32 ; 16-byte Folded Spill cbnz w8, LBB0_2
b LBB0_1
LBB0_1:
b LBB0_5
LBB0_2:
b.ne LBB0_4
b LBB0_3
LBB0_3:
b LBB0_5
LBB0_4:
bl _fib
bl _fib
add w8, w8, w9
b LBB0_5
LBB0_5:
ldp x29, x30, sp, #32 ; 16-byte Folded Reload ret
.globl _main
.p2align 2
_main:
; スタック領域確保
; x29とx30の値をスタックへ待避
stp x29, x30, sp, #16 ; 16-byte Folded Spill ; fib の第一引数は w0 へセットする感じ?
; fib の戻り値は w0 にセットされているっぽい
bl _fib
; スタックへ待避したx29とx30の値を復元
ldp x29, x30, sp, #16 ; 16-byte Folded Reload ; スタックを戻す
; fib の戻り値 w0 をそのまま返す
ret
登場人物
sp
スタックポインタとして用いるレジスタ
str
ストア命令。レジスタに格納されてる値をメモリに書き込む
stp
二つのレジスタの値をメモリ中の指定したアドレスへ書き込む
stur
メモリ中の指定したアドレスへレジススタの値を書き込む
(strとの違いがよく分からない...)
wzr
32ビット幅のゼロレジスタ。読み込むと常に0を返す。64ビット幅のゼロレジスタは xzr で呼び出す
mov
レジスタからレジスタへの値のコピー、即値のロードなどに利用する
w0
32ビット幅の0番レジスタ。64ビット幅の0番レジスタは x0 で呼び出す
ret
return。指定したレジスタのアドレスに無条件分岐する。レジスタを指定しない場合はリンクレジスタ x30 を指定したことになる
bl
関数呼び出し的な?
参考
ARM命令セット
Linux で Arm64 アセンブリプログラミング (05) ストア命令
Arm64のレジスタ
分岐命令